#include "Box.h"


Box::Box(vec4 _pos, vec3 _rot, vec3 _min, vec3 _max):Shape(_pos, _rot){
	extents[0] = _min;
	extents[1] = _max;
	//transform the min and max vectors and recalculate the bounds
	//vec3 temp_min = vec3(invModelviewMat * vec4(extents[0], 1));	//puts the vector into world space
	//vec3 temp_max = vec3(invModelviewMat * vec4(extents[1], 1));
	//get all points of the box in world space and calculate the new bounds
	vec4 points[8];
	points[0] = vec4(extents[0].x, extents[0].y, extents[0].z, 1);
	points[1] = vec4(extents[1].x, extents[0].y, extents[0].z, 1);
	points[2] = vec4(extents[1].x, extents[0].y, extents[1].z, 1);
	points[3] = vec4(extents[0].x, extents[0].y, extents[1].z, 1);
	points[4] = vec4(extents[0].x, extents[1].y, extents[0].z, 1);
	points[5] = vec4(extents[1].x, extents[1].y, extents[0].z, 1);
	points[6] = vec4(extents[1].x, extents[1].y, extents[1].z, 1);
	points[7] = vec4(extents[0].x, extents[1].y, extents[1].z, 1);
	minBound = vec3(1e30f);
	maxBound = vec3(-1e30f);
	for (int i = 0; i < 8; i++){
		//transform the vertex into world space
		vec4 worldPt = invModelviewMat * points[i];
		minBound.x = min(minBound.x, worldPt.x);
		minBound.y = min(minBound.y, worldPt.y);
		minBound.z = min(minBound.z, worldPt.z);
		maxBound.x = max(maxBound.x, worldPt.x);
		maxBound.y = max(maxBound.y, worldPt.y);
		maxBound.z = max(maxBound.z, worldPt.z);
	}
	//now min and max bound are in world space
}


Box::~Box(){
}

bool Box::intersect(Intersection &_int){
	Stats::instance()->primTest();	//increment the intersection test count
	//get the ray
	Ray _theRay = *(_int.ray());	
	
	//transform ray into box space if it is rotated
	Ray t_ray = _theRay;
	t_ray.transform(modelviewMat);
	vec3 rayOrig = t_ray.origin();
	vec3 rayDir = t_ray.direction();
	vec3 invDir = t_ray.invDirection();

	float tmin, tmax, tymin, tymax, tzmin, tzmax;
	//perform the intersection test
	tmin = (extents[t_ray.sign[0]].x - rayOrig.x) * invDir.x;
	tmax = (extents[1 - t_ray.sign[0]].x - rayOrig.x) * invDir.x;
	tymin = (extents[t_ray.sign[1]].y - rayOrig.y) * invDir.y;
	tymax = (extents[1 - t_ray.sign[1]].y - rayOrig.y) * invDir.y;
	if ((tmin > tymax) || (tymin > tmax))return false;
	if (tymin > tmin){
		tmin = tymin;
	}
	if (tymax < tmax){
		tmax = tymax;
	}
	tzmin = (extents[t_ray.sign[2]].z - rayOrig.z) * invDir.z;
	tzmax = (extents[1 - t_ray.sign[2]].z - rayOrig.z) * invDir.z;
	if ((tmin > tzmax) || (tzmin > tmax))return false;
	if (tzmin > tmin){
		tmin = tzmin;
	}
	if (tzmax < tmax){
		tmax = tzmax;
	}
	
	if (tmax < 0)return false;	//the box is behind the ray
	if (tmax > t_ray.maxDist() || tmin < t_ray.minDist())return false;	//the intersection is too far away or too close

	float _dist = (tmin >= 0) ? tmin : tmax;

	//disregard the minimum distance here; the bounding box is the same as the box if they are both axis-aligned
	if (_dist < _int.dist()){
		//set the new closest distance
		_int.dist(_dist);
		//set the new closest shape
		_int.shape(this);
		//set the surface normal at the intersection point
		vec3 _pt = _theRay.pointAtDepth(_dist);

		//IN BOX SPACE
		//get the spherical surface normal, isolate the largest component, and make that the new surface normal
		vec3 temp_pt = t_ray.pointAtDepth(_dist);
		vec3 temp_norm = abs(temp_pt);
		
		vec3 _norm;
		if (temp_norm.x > temp_norm.y && temp_norm.x > temp_norm.z){
			//x is greatest
			if (temp_pt.x > 0)
				_norm = vec3(1, 0, 0);
			else
				_norm = vec3(-1, 0, 0);
		}
		else if (temp_norm.y > temp_norm.x && temp_norm.y > temp_norm.z){
			//y is greatest
			if (temp_pt.y > 0)
				_norm = vec3(0, 1, 0);
			else
				_norm = vec3(0, -1, 0);
		}
		else{
			//z is greatest
			if (temp_pt.z > 0)
				_norm = vec3(0, 0, 1);
			else
				_norm = vec3(0, 0, -1);
		}
		//normalize the normal
		//_norm = normalize(_norm);	//this is already normalized
		//transform _norm back into world space
		_norm = vec3(invModelviewMat * vec4(_norm, 0));

		_int.surfNorm(_norm);
	}

	_int.intersected(true);
	Stats::instance()->primInt();	//increment the intersection count
	return true;

}

vec4 Box::getColor(Intersection &_int){
	if (shade == NULL){
		return vec4(0, 0.4, 0, 0);
	}
	return shade->getColor(_int);
}

vec3 Box::getMaxBound(){
	return maxBound;
}
vec3 Box::getMinBound(){ 
	return minBound;
}

vec3 Box::getCenter(){
	return pos.xyz;
}
